1、 tty 设备节点命名规则
	/dev/ttyS0 、 /dev/ttySTM0 ：串口终端
	/dev/tty1 、 /dev/tty2 、 …… ：虚拟终端设备
	/dev/tty0 ：当前正在使用的虚拟终端的别名
	/dev/tty ：本进程自己的终端
	/dev/console ：控制台，由内核的命令行参数确定，可以认为是一个拥有更高权限的终端，不管当前正在使用哪个终
端，系统信息都会发送到控制台上
2、串口编程
	1）头文件
		#include <stdio.h>   /* Standard input/output definitions */
		#include <string.h>  /* String function definitions */
		#include <unistd.h>  /* UNIX standard function definitions */
		#include <fcntl.h>   /* File control definitions */
		#include <errno.h>   /* Error number definitions */
		#include <termios.h> /* POSIX terminal control definitions */
	2）打开串口
		int open_port(void)
		{
			int fd;
			/* 打开串口
			 * "/dev/ttyf1" 串口文件名
			 * O_RDWR 以读写方式打开
			 * O_NOCTTY 不将此端口作为控制终端
			 * O_NDELAY 表示不关心 DCD 信号线的状态，同时它还将串口设置为非阻塞模式，在没有数据时都操作返回0，
			 *          后面可以通过fcntl(fd, F_SETFL, 0)将其设置为阻塞式
			 **/
			fd = open("/dev/ttyf1", O_RDWR | O_NOCTTY | O_NDELAY);
			if (fd == -1)
			{
				/* 打开失败 */
				perror("open_port: Unable to open /dev/ttyf1 - ");
			}
			else
			{
				/* 设置为阻塞式读取 */
				fcntl(fd, F_SETFL, 0);
			}
			
			return (fd);
		}
	3）写数据
		通过write函数写数据
	4）读数据
		通过read函数都数据
	6）关闭串口
		通过close函数关闭
	7）配置串口
		可以通过如下来读取或配置串口参数：
			/* tcgetattr 获取串口配置参数， tcsetattr 设置串口配置参数
			* fd
			* optional_actions 配置模式：
			*                  TCSANOW 改变立即发生 
			*                  TCSADRAIN 改变在写入 fd 的数据都被传输后生效
			*                  TCSAFLUSH 改变在写入 fd 的数据都被传输后生效，且已接收但未读取的数据全部丢弃
			* termios_p 串口配置参数
			**/
			int tcgetattr(int fd, struct termios *termios_p)
			int tcsetattr(int fd, int optional_actions, const struct termios *termios_p)
		串口的配置参数保存在struct termios 结构体中，此结构体至少包含以下成员：
			/* 输入控制标志 */
			tcflag_t c_iflag;
			/* 输出控制标志 */
			tcflag_t c_oflag;
			/* 控制模式标志 */
			tcflag_t c_cflag;
			/* 本地模式标志 */
			tcflag_t c_lflag;
			/* 行规程 */
			cc_t c_cc[NCCS];
			/* 输入波特率 */
			int c_ispeed;
			/* 输出波特率 */
			int c_ospeed;
	a）输入控制标志选项（c_iflag）
		INPCK ：启用输入奇偶校验
		IGNPAR ：忽略奇偶校验错误
		PARMRK ：标记奇偶校验错误
		ISTRIP ：去掉奇偶校验位
		IXON ：启用输出的 XON/XOFF 流控制
		IXOFF ：启用输入的 XON/XOFF 流控制
		IXANY ：允许任何字符来重新开始输出
		IGNBRK ：忽略输入中的 BREAK 状态
		BRKINT ：检测到中断条件时发送 SIGINT
		INLCR ：将换行映射到回车
		IGNCR ：忽略回车
		ICRNL ：将回车映射到换行
		IUCLC ：将大写映射到小写
		启用输入奇偶校验并剥离奇偶校验位：
			c_iflag |= (INPCK | ISTRIP)
		启用软件流控制：
			c_iflag |= (IXON | IXOFF | IXANY);
		禁用软件流控：
			c_iflag &= ~(IXON | IXOFF | IXANY);
	b）输出控制标志选项（c_oflag）
		OPOST ：启用输出处理（未设置 = 原始输出）
		OLCUC ：将小写映射到大写
		ONLCR ：将换行映射到回车+换行
		OCRNL ：将回车映射到换行
		NOCR ：不在第 0 列输出回车
		ONLRET ：将换行映射到回车
		OFILL ：发送填充字符作为延时，而不是使用定时来延时
		OFDEL ：填充字符为 DEL
		NLDLY ：新行延时掩码
		NL0 ：新行没有延迟
		NL1 ：换行后延迟100ms
		CRDLY ：回车延时掩码
		CR0 ：回车没有延迟
		CR1 ：回车后的延迟取决于当前列位置
		CR2 ：回车后延迟 100 毫秒
		CR3 ：回车后延迟 150 毫秒
		TABDLY ：tab延时掩码
		TAB0 ：TAB 没有延迟
		TAB1 ：TAB 后的延迟取决于当前列位置
		TAB2 ：发送 TAB 后延迟 100 毫秒
		TAB3 ：将 TAB 字符扩展为空格
		BSDLY ：回退延时掩码
		BS0 ：回退没有延迟
		BS1 ：回退后延迟 50 毫秒
		VTDLY ：竖直跳格延时掩码
		VT0 ：竖直跳格无延迟
		VT1 ：竖直跳格后延迟 2 秒
		FFDLY ：进表延时掩码
		FF0 ：进表没有延迟
		FF1 ：进表后延迟 2 秒
		选择原始输出：
			/* 禁用OPOST选项时，将忽略 c_oflag中的所有其他选项位 */
			c_oflag &= ~OPOST;
	c）控制模式标志（c_cflag）
		CSIZE ：字符长度掩码
		CS5 ：5 个数据位
		CS6 ：6 个数据位
		CS7 ：7 个数据位
		CS8 ：8 个数据位
		CSTOPB ：2 个停止位（否则为 1 个）
		CREAD ：启用接收器
		PARENB ：启用奇偶校验位
		PARODD ：使用奇校验而不是偶校验
		HUPCL ：关闭时挂断 moden
		CLOCAL ：忽略 modem 控制线
		CRTSCTS ：启用硬件流控制
		设置字符大小：
			c_cflag &= ~CSIZE; /* 屏蔽字符大小位 */ 
			c_cflag |= CS8; /* 选择 8 个数据位 */
		设置奇偶校验：
			/*无奇偶校验 (8N1) */
			c_cflag &= ~PARENB;
			c_cflag &= ~CSTOPB;
			c_cflag &= ~CSIZE;
			c_cflag |= CS8;
			/* 偶校验（7E1） */
			c_cflag |= PARENB;
			c_cflag &= ~PARODD;
			c_cflag &= ~CSTOPB;
			c_cflag &= ~CSIZE;
			c_cflag |= CS7;
			/* 奇校验（7O1） */
			c_cflag |= PARENB;
			c_cflag |= PARODD;
			c_cflag &= ~CSTOPB;
			c_cflag &= ~CSIZE;
			c_cflag |= CS7;
		设置硬件流控制：
			c_cflag |= CRTSCTS;
		禁用硬件流控制：
			c_cflag &= ~CRTSCTS;
	d）本地模式标志（c_lflag）
		ISIG ：启用 SIGINTR、SIGSUSP、SIGDSUSP 和 SIGQUIT 信号
		ICANON ：使能规范输入，这使 EOF 、 EOL 、 EOL2 、 ERASE 、 KILL 、 REPRINT 、 STATUS 、 WERASE 字符
	起作用，输入字符被装配成行
		XCASE ：如果同时设置了 ICANON ，则输入被转换为小写（除有前缀 / 的字符以外），输出一个大写
	字符也在其前加一个 /
		ECHO ：回显输入字符
		ECHOE ：如果同时设置了 ICANON ，字符 ERASE 擦除前一个输入字符， WERASE 擦除前一个词
		ECHOK ：如果同时设置了 ICANON，字符 KILL 删除当前行
		ECHONL ：如果同时设置了 ICANON，回显字符 NL，即使没有设置 ECHO
		NOFLSH ：禁止在产生 SIGINT, SIGQUIT 和 SIGSUSP 信号时刷新输入和输出队列
		IEXTEN ：启用扩展字符处理，这个标志必须与 ICANON 同时使用才能解释特殊字符 EOL2 、 LNEXT 、 REPRINT 、
	WERASE， IUCLC 标志才有效
		ECHOCTL ：如果同时设置了 ECHO，除了 TAB, NL, START, 和 STOP 之外的 ASCII 控制字符被回显为 ^X （这里 
	X 是比控制字符加 0x40 的 ASCII 码）
		ECHOPRT ：如果同时设置了 ICANON 和 IECHO，字符在删除的同时被打印
		ECHOKE ：如果同时设置了 ICANON ，回显 KILL 时将删除一行中的每个字符
		FLUSHO ：输出被刷新，这个标志可以通过键入字符 DISCARD 来开关
		PENDIN ：在读入下一个字符时，输入队列中所有字符被重新输出
		TOSTOP ：向试图写控制终端的后台进程组发送 SIGTTOU 信号
		选择规范输入：
			c_lflag |= (ICANON | ECHO | ECHOE);
		选择原始输入：
			c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
	e）控制字符（c_cc ）
		VINTR ：中断字符。发出 SIGINT 信号，当设置 ISIG 时可被识别
		VQUIT ：退出字符。发出 SIGQUIT 信号，当设置 ISIG 时可被识别
		VERAS ：删除字符。删除上一个还没有删掉的字符，但不删除上一个 EOF 或行首，当设置 ICANON 时可被识别
		VKILL ：终止字符。删除自上一个 EOF 或行首以来的输入，当设置 ICANON 时可被识别
		VEOF ：文件尾字符。这个字符使得 tty 缓冲中的内容被送到等待输入的用户程序中，而不必等到 EOL，当设置 
	ICANON 时可被识别
		VEOL ： 行结束字符，当设置 ICANON 时可被识别
		VEOL2 ：替换的行结束，当设置 ICANON 时可被识别
		VMIN ：要读取的最小字符数
		VTIME ：等待数据的时间（100毫秒）
		MIN与TIME组合有以下四种：
			（1）MIN = 0 , TIME = 0
				有数据立即读取，并返回读取的字节数，无数据立即返回0
			（2）MIN = 0 , TIME > 0
				在 TIME 指定的时间内有数据则返回读取的字节数，无数据返回0
			（3）MIN > 0 , TIME = 0
				在最少读取到 MIN 个字节数才返回
			（4）MIN > 0 , TIME > 0
				读取到的一个字节时启动计时，此后每收到一个字符都会重新计时，在最少读取到 MIN 个字节数或超时
			返回读取的字节数
	f）波特率（c_ispeed & c_ospeed）
		波特率的设置和读取通过下列函数实现：
			/* 获取输入波特率 */
			speed_t cfgetispeed(const struct termios *termios_p);
			/* 获取输出波特率 */
			speed_t cfgetospeed(const struct termios *termios_p);
			/* 设置输入波特率 */
			int cfsetispeed(struct termios *termios_p, speed_t speed);
			/* 设置输出波特率 */
			int cfsetospeed(struct termios *termios_p, speed_t speed);
			/* 设置输入输出波特率 */
			int cfsetspeed(struct termios *termios_p, speed_t speed);
